Introduction

Contexte et Obejectif :

Groupama est une entreprise d’assurance française. En tant qu’alternant Data Analyst chez Groupama, et plus précisément chez Groupama Gan Vie (GGVie), unique filiale d’Assurance de Personnes du Groupe, je suis amené à utiliser toute sorte de données sensibles sur nos clients. Cela peut être les noms, adresses, numéros de compte, numéros de contrat, les montants, les données de nos commerciaux, etc. Malheureusement, GGVie est assez fermé et je n’ai pas pu récupérer leurs données, et je ne sais pas si j’aurais les droits un jour. En revanche, un collègue, qui est aussi en alternance et qui à les mêmes problèmes d’accès aux données que moi, m’a parlé d’un site http://insideairbnb.com/ qui recense des données sur les biens locatifs présents sur AirBnB, et dans plusieurs villes. Les données téléchargeables sur le site Inside Airbnb proviennent d’informations accessibles au public sur le site Airbnb. La base de données que je vais utiliser contient un panel d’information sur les biens en location sur Paris, des données qui se rapprochent de ce que je peux utiliser dans la vraie vie.

Dans ce rapport, nous allons réaliser une étude des prix en locatif dans Paris et essayer de trouver ce qui peut influencer le prix. Nous allons aussi essayer de faire des cartes, car je n’ai jamais eu l’occasion de faire ce genre de représentation sur R

Libraries

pkgLoad <- function( packages = "favourites" ) {
  
  if( length( packages ) == 1L && packages == "favourites" ) {
    packages <- c(
     "readxl", 
     "readr", 
     "tidyr", 
     "ggplot2", 
     "ggmap", 
     "dplyr",
     "sf", 
     "visdat", 
     "leaflet", 
     "maps", 
     "stringr",
     "questionr", 
     "RColorBrewer",
     "GGally"
    )
  }
  
  packagecheck <- match( packages, utils::installed.packages()[, 1] )
  
  packagestoinstall <- packages[ is.na( packagecheck ) ]
  
  if( length( packagestoinstall ) > 0L ) {
    utils::install.packages( packagestoinstall,
                             repos = "https://pbil.univ-lyon1.fr/CRAN/"
    )
  } else {
    print( "All requested packages already installed" )
  }
  
  for( package in packages ) {
    suppressPackageStartupMessages(
      library( package, character.only = TRUE, quietly = TRUE )
    )
  }
  
}


pkgLoad()
## [1] "All requested packages already installed"
## ℹ Google's Terms of Service: <]8;;https://mapsplatform.google.comhttps://mapsplatform.google.com]8;;>
## ℹ Please cite ggmap if you use it! Use `citation("ggmap")` for details.
#Liste des librairies nécessaire 
require(readxl) #import de mes données
require(readr) #import de mes données
require(tidyr) #
require(ggplot2) #analyse descriptive/ graphique
require(ggmap) #cartographie via google
require(dplyr)
require(sf) #cartographie
require(visdat) #Visualisation valeurs maquantes
require(leaflet) #Cartographie
require(maps) #Cartographie
require(stringr) #Manipulation string 
require(questionr) 
require(RColorBrewer) #couleurs supplémetaires
require(GGally) #Matrice des corrélations

Les Données

Présentation des données

Nous allons charger le jeu de données, mais aussi son dictionnaire qui contient pour chaque variable sa signification.

#Chemin et chargement repertoire de travail
path <- file.path("/Applications", "Dossier", "cours", "Master MIASHS","R_analyse","data", fsep="/")
setwd(path)
#Import des données
df <- read_csv("listings.csv")
## Rows: 61365 Columns: 75
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr  (25): listing_url, source, name, description, neighborhood_overview, pi...
## dbl  (37): id, scrape_id, host_id, host_listings_count, host_total_listings_...
## lgl   (8): host_is_superhost, host_has_profile_pic, host_identity_verified, ...
## date  (5): last_scraped, host_since, calendar_last_scraped, first_review, la...
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
#Import du dictionnaire de nos données
Dictionnaire <- read_excel("Inside Airbnb Data Dictionary.xlsx", 
                           range = "A9:D83", col_names = FALSE)
## New names:
## • `` -> `...1`
## • `` -> `...2`
## • `` -> `...3`
## • `` -> `...4`
#On modifie le noms de nos colonnes pour le dictionnaire
nomscol <- c("NomVar", "Type", "Calculated", "Description")
colnames(Dictionnaire) <- nomscol

#listing de nos variables
Dictionnaire$NomVar
##  [1] "id"                                          
##  [2] "listing_url"                                 
##  [3] "scrape_id"                                   
##  [4] "last_scraped"                                
##  [5] "source"                                      
##  [6] "name"                                        
##  [7] "description"                                 
##  [8] "neighborhood_overview"                       
##  [9] "picture_url"                                 
## [10] "host_id"                                     
## [11] "host_url"                                    
## [12] "host_name"                                   
## [13] "host_since"                                  
## [14] "host_location"                               
## [15] "host_about"                                  
## [16] "host_response_time"                          
## [17] "host_response_rate"                          
## [18] "host_acceptance_rate"                        
## [19] "host_is_superhost"                           
## [20] "host_thumbnail_url"                          
## [21] "host_picture_url"                            
## [22] "host_neighbourhood"                          
## [23] "host_listings_count"                         
## [24] "host_total_listings_count"                   
## [25] "host_verifications"                          
## [26] "host_has_profile_pic"                        
## [27] "host_identity_verified"                      
## [28] "neighbourhood"                               
## [29] "neighbourhood_cleansed"                      
## [30] "neighbourhood_group_cleansed"                
## [31] "latitude"                                    
## [32] "longitude"                                   
## [33] "property_type"                               
## [34] "room_type"                                   
## [35] "accommodates"                                
## [36] "bathrooms"                                   
## [37] "bathrooms_text"                              
## [38] "bedrooms"                                    
## [39] "beds"                                        
## [40] "amenities"                                   
## [41] "price"                                       
## [42] "minimum_nights"                              
## [43] "maximum_nights"                              
## [44] "minimum_minimum_nights"                      
## [45] "maximum_minimum_nights"                      
## [46] "minimum_maximum_nights"                      
## [47] "maximum_maximum_nights"                      
## [48] "minimum_nights_avg_ntm"                      
## [49] "maximum_nights_avg_ntm"                      
## [50] "calendar_updated"                            
## [51] "has_availability"                            
## [52] "availability_30"                             
## [53] "availability_60"                             
## [54] "availability_90"                             
## [55] "availability_365"                            
## [56] "calendar_last_scraped"                       
## [57] "number_of_reviews"                           
## [58] "number_of_reviews_ltm"                       
## [59] "number_of_reviews_l30d"                      
## [60] "first_review"                                
## [61] "last_review"                                 
## [62] "review_scores_rating"                        
## [63] "review_scores_accuracy"                      
## [64] "review_scores_cleanliness"                   
## [65] "review_scores_checkin"                       
## [66] "review_scores_communication"                 
## [67] "review_scores_location"                      
## [68] "review_scores_value"                         
## [69] "license"                                     
## [70] "instant_bookable"                            
## [71] "calculated_host_listings_count"              
## [72] "calculated_host_listings_count_entire_homes" 
## [73] "calculated_host_listings_count_private_rooms"
## [74] "calculated_host_listings_count_shared_rooms" 
## [75] "reviews_per_month"

On a 75 variables pour 61365 observations.

Nettoyage des données

Premier tri des variables a analyser

Comme nous avons beaucoup de données, nous allons faire une sélection et supprimer celles qui ne nous servirons pas. Nous enlevons celles qui sont inutiles, soit parce qu’elles ne nous apportent aucune information intéressante (url par exemple, ou la variable description des biens qui est un petit texte libre de l’utilisateur, etc), soit parce que des variables sont vides (bathrooms par exemple).

summary(df)
##        id            listing_url          scrape_id        
##  Min.   :5.396e+03   Length:61365       Min.   :2.022e+13  
##  1st Qu.:1.509e+07   Class :character   1st Qu.:2.022e+13  
##  Median :3.464e+07   Mode  :character   Median :2.022e+13  
##  Mean   :1.309e+17                      Mean   :2.022e+13  
##  3rd Qu.:5.236e+07                      3rd Qu.:2.022e+13  
##  Max.   :7.120e+17                      Max.   :2.022e+13  
##                                                            
##   last_scraped           source              name           description       
##  Min.   :2022-09-09   Length:61365       Length:61365       Length:61365      
##  1st Qu.:2022-09-10   Class :character   Class :character   Class :character  
##  Median :2022-09-10   Mode  :character   Mode  :character   Mode  :character  
##  Mean   :2022-09-09                                                           
##  3rd Qu.:2022-09-10                                                           
##  Max.   :2022-09-15                                                           
##                                                                               
##  neighborhood_overview picture_url           host_id         
##  Length:61365          Length:61365       Min.   :      275  
##  Class :character      Class :character   1st Qu.: 15133737  
##  Mode  :character      Mode  :character   Median : 47563243  
##                                           Mean   :121183202  
##                                           3rd Qu.:193397737  
##                                           Max.   :478775258  
##                                                              
##    host_url          host_name           host_since         host_location     
##  Length:61365       Length:61365       Min.   :2008-04-17   Length:61365      
##  Class :character   Class :character   1st Qu.:2014-05-12   Class :character  
##  Mode  :character   Mode  :character   Median :2015-11-05   Mode  :character  
##                                        Mean   :2016-06-09                     
##                                        3rd Qu.:2018-06-04                     
##                                        Max.   :2022-09-09                     
##                                        NA's   :7                              
##   host_about        host_response_time host_response_rate host_acceptance_rate
##  Length:61365       Length:61365       Length:61365       Length:61365        
##  Class :character   Class :character   Class :character   Class :character    
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character    
##                                                                               
##                                                                               
##                                                                               
##                                                                               
##  host_is_superhost host_thumbnail_url host_picture_url   host_neighbourhood
##  Mode :logical     Length:61365       Length:61365       Length:61365      
##  FALSE:53647       Class :character   Class :character   Class :character  
##  TRUE :7674        Mode  :character   Mode  :character   Mode  :character  
##  NA's :44                                                                  
##                                                                            
##                                                                            
##                                                                            
##  host_listings_count host_total_listings_count host_verifications
##  Min.   :   0.00     Min.   :   1.00           Length:61365      
##  1st Qu.:   1.00     1st Qu.:   1.00           Class :character  
##  Median :   1.00     Median :   2.00           Mode  :character  
##  Mean   :  16.43     Mean   :  26.27                             
##  3rd Qu.:   2.00     3rd Qu.:   4.00                             
##  Max.   :1500.00     Max.   :2302.00                             
##  NA's   :7           NA's   :7                                   
##  host_has_profile_pic host_identity_verified neighbourhood     
##  Mode :logical        Mode :logical          Length:61365      
##  FALSE:722            FALSE:10210            Class :character  
##  TRUE :60636          TRUE :51148            Mode  :character  
##  NA's :7              NA's :7                                  
##                                                                
##                                                                
##                                                                
##  neighbourhood_cleansed neighbourhood_group_cleansed    latitude    
##  Length:61365           Mode:logical                 Min.   :48.80  
##  Class :character       NA's:61365                   1st Qu.:48.85  
##  Mode  :character                                    Median :48.87  
##                                                      Mean   :48.86  
##                                                      3rd Qu.:48.88  
##                                                      Max.   :48.91  
##                                                                     
##    longitude     property_type       room_type          accommodates   
##  Min.   :2.206   Length:61365       Length:61365       Min.   : 0.000  
##  1st Qu.:2.320   Class :character   Class :character   1st Qu.: 2.000  
##  Median :2.347   Mode  :character   Mode  :character   Median : 2.000  
##  Mean   :2.344                                         Mean   : 3.052  
##  3rd Qu.:2.371                                         3rd Qu.: 4.000  
##  Max.   :2.488                                         Max.   :16.000  
##                                                                        
##  bathrooms      bathrooms_text        bedrooms           beds       
##  Mode:logical   Length:61365       Min.   : 1.000   Min.   : 1.000  
##  NA's:61365     Class :character   1st Qu.: 1.000   1st Qu.: 1.000  
##                 Mode  :character   Median : 1.000   Median : 1.000  
##                                    Mean   : 1.373   Mean   : 1.733  
##                                    3rd Qu.: 2.000   3rd Qu.: 2.000  
##                                    Max.   :50.000   Max.   :90.000  
##                                    NA's   :9972     NA's   :964     
##   amenities            price           minimum_nights    maximum_nights    
##  Length:61365       Length:61365       Min.   :   1.00   Min.   :       1  
##  Class :character   Class :character   1st Qu.:   2.00   1st Qu.:      90  
##  Mode  :character   Mode  :character   Median :   3.00   Median :    1125  
##                                        Mean   :  81.19   Mean   :     848  
##                                        3rd Qu.:  30.00   3rd Qu.:    1125  
##                                        Max.   :9999.00   Max.   :10000000  
##                                                                            
##  minimum_minimum_nights maximum_minimum_nights minimum_maximum_nights
##  Min.   :   1.00        Min.   :   1.0         Min.   :1.000e+00     
##  1st Qu.:   2.00        1st Qu.:   2.0         1st Qu.:9.900e+01     
##  Median :   3.00        Median :   4.0         Median :1.125e+03     
##  Mean   :  80.46        Mean   :  84.2         Mean   :1.409e+05     
##  3rd Qu.:  30.00        3rd Qu.:  30.0         3rd Qu.:1.125e+03     
##  Max.   :9999.00        Max.   :9999.0         Max.   :2.147e+09     
##  NA's   :6              NA's   :6              NA's   :6             
##  maximum_maximum_nights minimum_nights_avg_ntm maximum_nights_avg_ntm
##  Min.   :1.000e+00      Min.   :   1.00        Min.   :1.000e+00     
##  1st Qu.:3.650e+02      1st Qu.:   2.00        1st Qu.:2.240e+02     
##  Median :1.125e+03      Median :   3.00        Median :1.125e+03     
##  Mean   :5.259e+05      Mean   :  83.71        Mean   :2.792e+05     
##  3rd Qu.:1.125e+03      3rd Qu.:  30.00        3rd Qu.:1.125e+03     
##  Max.   :2.147e+09      Max.   :9999.00        Max.   :2.147e+09     
##  NA's   :6              NA's   :6              NA's   :6             
##  calendar_updated has_availability availability_30  availability_60
##  Mode:logical     Mode :logical    Min.   : 0.000   Min.   : 0.00  
##  NA's:61365       FALSE:107        1st Qu.: 0.000   1st Qu.: 0.00  
##                   TRUE :61258      Median : 0.000   Median : 0.00  
##                                    Mean   : 3.936   Mean   :10.88  
##                                    3rd Qu.: 4.000   3rd Qu.:15.00  
##                                    Max.   :30.000   Max.   :60.00  
##                                                                    
##  availability_90 availability_365 calendar_last_scraped number_of_reviews
##  Min.   : 0.00   Min.   :  0.0    Min.   :2022-09-09    Min.   :   0.00  
##  1st Qu.: 0.00   1st Qu.:  0.0    1st Qu.:2022-09-10    1st Qu.:   1.00  
##  Median : 1.00   Median : 26.0    Median :2022-09-10    Median :   6.00  
##  Mean   :20.06   Mean   :106.5    Mean   :2022-09-09    Mean   :  23.91  
##  3rd Qu.:38.00   3rd Qu.:231.0    3rd Qu.:2022-09-10    3rd Qu.:  24.00  
##  Max.   :90.00   Max.   :365.0    Max.   :2022-09-15    Max.   :2391.00  
##                                                                          
##  number_of_reviews_ltm number_of_reviews_l30d  first_review       
##  Min.   :   0.000      Min.   : 0.0000        Min.   :2009-06-30  
##  1st Qu.:   0.000      1st Qu.: 0.0000        1st Qu.:2017-04-20  
##  Median :   1.000      Median : 0.0000        Median :2019-06-18  
##  Mean   :   7.229      Mean   : 0.6423        Mean   :2019-04-13  
##  3rd Qu.:   8.000      3rd Qu.: 1.0000        3rd Qu.:2021-11-20  
##  Max.   :1356.000      Max.   :92.0000        Max.   :2022-09-09  
##                                               NA's   :11880       
##   last_review         review_scores_rating review_scores_accuracy
##  Min.   :2010-05-28   Min.   :0.000        Min.   :0.000         
##  1st Qu.:2020-01-03   1st Qu.:4.500        1st Qu.:4.680         
##  Median :2022-07-10   Median :4.790        Median :4.880         
##  Mean   :2021-04-18   Mean   :4.614        Mean   :4.753         
##  3rd Qu.:2022-08-21   3rd Qu.:5.000        3rd Qu.:5.000         
##  Max.   :2022-09-10   Max.   :5.000        Max.   :5.000         
##  NA's   :11880        NA's   :11880        NA's   :12452         
##  review_scores_cleanliness review_scores_checkin review_scores_communication
##  Min.   :0.000             Min.   :0.00          Min.   :0.00               
##  1st Qu.:4.440             1st Qu.:4.75          1st Qu.:4.77               
##  Median :4.750             Median :4.92          Median :4.94               
##  Mean   :4.591             Mean   :4.79          Mean   :4.80               
##  3rd Qu.:4.960             3rd Qu.:5.00          3rd Qu.:5.00               
##  Max.   :5.000             Max.   :5.00          Max.   :5.00               
##  NA's   :12450             NA's   :12462         NA's   :12451              
##  review_scores_location review_scores_value   license          instant_bookable
##  Min.   :0.000          Min.   :0.000       Length:61365       Mode :logical   
##  1st Qu.:4.710          1st Qu.:4.500       Class :character   FALSE:41088     
##  Median :4.900          Median :4.710       Mode  :character   TRUE :20277     
##  Mean   :4.785          Mean   :4.611                                          
##  3rd Qu.:5.000          3rd Qu.:4.890                                          
##  Max.   :5.000          Max.   :5.000                                          
##  NA's   :12464          NA's   :12466                                          
##  calculated_host_listings_count calculated_host_listings_count_entire_homes
##  Min.   :  1.00                 Min.   :  0.0                              
##  1st Qu.:  1.00                 1st Qu.:  1.0                              
##  Median :  1.00                 Median :  1.0                              
##  Mean   : 11.08                 Mean   : 10.4                              
##  3rd Qu.:  2.00                 3rd Qu.:  1.0                              
##  Max.   :269.00                 Max.   :269.0                              
##                                                                            
##  calculated_host_listings_count_private_rooms
##  Min.   : 0.000                              
##  1st Qu.: 0.000                              
##  Median : 0.000                              
##  Mean   : 0.495                              
##  3rd Qu.: 0.000                              
##  Max.   :67.000                              
##                                              
##  calculated_host_listings_count_shared_rooms reviews_per_month
##  Min.   : 0.00000                            Min.   : 0.010   
##  1st Qu.: 0.00000                            1st Qu.: 0.170   
##  Median : 0.00000                            Median : 0.570   
##  Mean   : 0.02855                            Mean   : 1.096   
##  3rd Qu.: 0.00000                            3rd Qu.: 1.480   
##  Max.   :24.00000                            Max.   :89.900   
##                                              NA's   :11880
#on voit que bathrooms contient 0 valeur
table(df$bathrooms)
## < table of extent 0 >
#liste qui contient les variables à supprimer 
delcol <- c("listing_url","scrape_id","last_scraped","source","description","neighborhood_overview","picture_url","host_url","host_about","host_thumbnail_url","host_picture_url"
            ,"host_neighbourhood","host_listings_count","neighbourhood_group_cleansed","minimum_minimum_nights","maximum_minimum_nights","minimum_maximum_nights","maximum_maximum_nights"
            ,"minimum_nights_avg_ntm","maximum_nights_avg_ntm","calendar_updated","availability_30","availability_60","availability_90","availability_365","calendar_last_scraped","license"
            ,"instant_bookable","calculated_host_listings_count","calculated_host_listings_count_entire_homes","calculated_host_listings_count_private_rooms","calculated_host_listings_count_shared_rooms"
            ,"reviews_per_month","bathrooms","amenities","first_review","last_review","review_scores_accuracy","host_response_time","host_response_rate","host_acceptance_rate"
            ,"host_verifications","host_has_profile_pic","number_of_reviews","number_of_reviews_ltm"      
            ,"number_of_reviews_l30d","review_scores_rating","review_scores_cleanliness","review_scores_checkin","review_scores_communication"
            )

#Suppression des valeurs colonnes dans le data base et en ligne dans notre dictionnaire 
df <- subset(df, select = -c(listing_url,scrape_id,last_scraped,source,description,neighborhood_overview,picture_url,host_url,host_about,host_thumbnail_url,host_picture_url
                             ,host_neighbourhood,host_listings_count,neighbourhood_group_cleansed,minimum_minimum_nights,maximum_minimum_nights,minimum_maximum_nights,maximum_maximum_nights
                             ,minimum_nights_avg_ntm,maximum_nights_avg_ntm,calendar_updated,availability_30,availability_60,availability_90,availability_365,calendar_last_scraped,license
                             ,instant_bookable,calculated_host_listings_count,calculated_host_listings_count_entire_homes,calculated_host_listings_count_private_rooms,calculated_host_listings_count_shared_rooms
                             ,reviews_per_month,bathrooms,amenities,first_review,last_review,review_scores_accuracy,host_response_time,host_response_rate,host_acceptance_rate
                             ,host_verifications,host_has_profile_pic,number_of_reviews,number_of_reviews_ltm,number_of_reviews_l30d,review_scores_rating
                             ,review_scores_cleanliness,review_scores_checkin,review_scores_communication
                             ))
Dictionnaire <- Dictionnaire[ ! Dictionnaire$NomVar %in% delcol, ]

head(df)
## # A tibble: 6 × 25
##       id name        host_id host_name host_since host_location host_is_superho…
##    <dbl> <chr>         <dbl> <chr>     <date>     <chr>         <lgl>           
## 1 130420 Charming A…  641777 Yassine   2011-05-30 Paris, France TRUE            
## 2  23441 Charming a…   91706 Elise     2010-03-12 Paris, France TRUE            
## 3   5396 Explore th…    7903 Borzou    2009-02-14 İstanbul, Tu… FALSE           
## 4 132994 charmant s…  653074 Victoire  2011-06-01 Paris, France FALSE           
## 5   7397 MARAIS - 2…    2626 Franck    2008-08-30 Paris, France TRUE            
## 6  24260 Lovely pla…   98012 Emmanuel  2010-03-23 Paris, France FALSE           
## # … with 18 more variables: host_total_listings_count <dbl>,
## #   host_identity_verified <lgl>, neighbourhood <chr>,
## #   neighbourhood_cleansed <chr>, latitude <dbl>, longitude <dbl>,
## #   property_type <chr>, room_type <chr>, accommodates <dbl>,
## #   bathrooms_text <chr>, bedrooms <dbl>, beds <dbl>, price <chr>,
## #   minimum_nights <dbl>, maximum_nights <dbl>, has_availability <lgl>,
## #   review_scores_location <dbl>, review_scores_value <dbl>

Finalement les valeurs que je garde sont :

Dictionnaire[,c(1,2,4)]
## # A tibble: 25 × 3
##    NomVar                    Type                      Description              
##    <chr>                     <chr>                     <chr>                    
##  1 id                        integer                   Airbnb's unique identifi…
##  2 name                      text                      Name of the listing      
##  3 host_id                   integer                   Airbnb's unique identifi…
##  4 host_name                 text                      Name of the host. Usuall…
##  5 host_since                date                      The date the host/user w…
##  6 host_location             text                      The host's self reported…
##  7 host_is_superhost         boolean [t=true; f=false] <NA>                     
##  8 host_total_listings_count text                      The number of listings t…
##  9 host_identity_verified    boolean [t=true; f=false] <NA>                     
## 10 neighbourhood             text                      <NA>                     
## # … with 15 more rows

Data Cleanning

vis_miss(df,warn_large_data=FALSE)#grapgique valeurs manquantes
## Warning: `gather_()` was deprecated in tidyr 1.2.0.
## Please use `gather()` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.

#ggsave("val_manquantes.png", width = 11, height = 8)

Grâce à ce graphique nous pouvons voir où sont nos données manquantes.

On voit déjà que la variable “neighbourhood”, contient presque 50% de données manquantes. Analysons la de plus près :

#tirage aléatoire de la variable neighbourhood et neighbourhood_cleansed pour une comparaison
df[sample(1:nrow(df),10),c(10,11)]
## # A tibble: 10 × 2
##    neighbourhood                    neighbourhood_cleansed
##    <chr>                            <chr>                 
##  1 <NA>                             Buttes-Montmartre     
##  2 <NA>                             Passy                 
##  3 Paris, Île-de-France, France     Popincourt            
##  4 <NA>                             Panthéon              
##  5 Paris, Île-de-France, France     Entrepôt              
##  6 Paris, Île-de-France, France     Panthéon              
##  7 Paris, Île-de-France, France     Buttes-Montmartre     
##  8 Montrouge, Île-de-France, France Observatoire          
##  9 <NA>                             Luxembourg            
## 10 <NA>                             Passy

En fait, la variable neighbourhood contient surtout la ville du bien. Étant donné que nous n’avons sélectionné que des biens sur Paris ou en proche banlieue, qu’en plus de cela, la variable “neighbourhood_cleansed” est plus précise sur le nom du quartier, et qu’elle ne contient pas de valeur manquante, on supprime simplement la variable “neighbourhood”. Pour le reste des valeurs manquantes on pourrait faire de l’imputation. Pour les variables de note on pourrait regarder si l’id hôte est déjà représenté dans la base et lui imputer la valeur qu’il a déjà. On pourrait également réaliser du machine learning pour l’imputation et faire une AFC, ou par les plus proches voisins. Comme on nous a demandé de rester simple et que mon jeu de données est suffisamment important on va simplement supprimer les données manquantes restantes.

df <- subset(df, select = -neighbourhood) #on supprime la variable neighbourhood
delcol <- c("neighbourhood")
Dictionnaire <- Dictionnaire[ ! Dictionnaire$NomVar %in% delcol,]
df <- df %>% drop_na() #on supprime les valeurs manquantes

On va recoder certaines variables pour qu’elles soient plus faciles à analyser, et qu’elles soient plus parlantes. Par exemple pour la variable host_location, on va la couper en deux pour ne garder que le pays d’origine. On se rend compte aussi que certain venant des USA ont mis le code de l’état dans lequel ils se trouvent, si la personne habite Atlanta, alors host_location prendra comme valeur “Atlanta, GA”, GA pour Georgie. On remodèle la variable bathrooms_text en 2 variables. La première étant le nombre de salle de bain, ou point d’eau d’un bien, la deuxième étant le type de salle de bain. Ou encore, on transforme la date d’inscription en nombre de jour inscrit

df <- df %>% separate(host_location, into=c('Ville', 'Pays'), sep=', ') #separation de la variable host_location en 2 
## Warning: Expected 2 pieces. Additional pieces discarded in 1 rows [24943].
## Warning: Expected 2 pieces. Missing pieces filled with `NA` in 341 rows [24,
## 109, 119, 194, 223, 361, 543, 577, 631, 670, 736, 745, 1188, 1216, 1334, 1551,
## 1587, 1816, 1905, 2382, ...].
listUSA = c("AK","AZ","AR","CA","NC","SC","CO","CT","ND","SD","DE","FL","GA","HI","ID","IL","IN","IA","KS","KY","LA","ME","MD","MA","MN","MS","MO","MT","NE","NV","NH","NJ","NY","NM","OH","OK","OR","PA","RI","TN","TX","UT","VT","VA","WV","WA","WI","WY") 
#liste des codes Etats des USA
reference <- as.Date("2022-12-01", format="%Y-%m-%d") #On stock une date de référence
passage = as.data.frame(str_split(df$bathrooms_text, " ",simplify=TRUE)) #split de la variable bathrooms_text
passage$TypeBath = paste(passage[,2],passage[,3])
ps = c("Shared","Private","Half-bath")
b = c("baths ","bath ","private bath")
hb = c("Half-bath","Private half-bath","Shared","Private"," half-bath ")
shb = c("shared baths","shared bath")
df$TypeBath = passage$TypeBath
df$NbBath = passage$V1

df <- df %>% mutate(Since = as.numeric(reference- as.Date(host_since,"%d-%m-%Y"))
                    ,PrixE = as.numeric(gsub("\\$", "", as.factor(gsub(",", "", df$price))))*0.98
                    ,Pays = ifelse(is.na(Pays)==TRUE, Ville, Pays)
                    ,Pays = ifelse(Pays %in% listUSA, "United States", Pays)
                    ,TypeBath = ifelse(NbBath =="Shared", "shared bath(s)", TypeBath)
                    ,TypeBath = ifelse(NbBath =="Half-bath", "Private half-bath", TypeBath)
                    ,TypeBath = ifelse(NbBath =="Private", "Private half-bath", TypeBath)
                    ,TypeBath = ifelse(TypeBath %in% b, "Private bath(s)", TypeBath)
                    ,TypeBath = ifelse(TypeBath %in% hb, "Private half-bath", TypeBath)
                    ,TypeBath = ifelse(TypeBath %in% shb, "shared bath(s)", TypeBath)
                    ,NbBath = as.numeric(ifelse(NbBath %in% ps, 0.5, NbBath))
                    ) #Recodage de nos variables

df <- subset(df, select = -c(Ville,price,host_since,bathrooms_text)) #suppressions des anciennes variables

Descriptif

Après le data cleannig, l’une partie les plus importante d’une analyse, nous allons passer véritablement à la partie analyse. Nous avons maintenant 34883 observations, une observation étant un bien, nous avons donc 34883 biens à observer. Pour commencer notre partie d’analyse, nous allons explorer sur une carte les biens enregistrés sur notre base.

basemap <- addTiles(leaflet()) #creatio d'une base de carte
map <- addCircleMarkers(setView(basemap, lng = 2.3522219, lat = 48.856614, zoom = 12), lng = df$longitude, lat = df$latitude, radius = 0.1, fillOpacity = 6, color ="Dark Grey")%>% addProviderTiles(providers$CartoDB.Positron) 
#localisation/ ajout des points sur notre carte 
map

On peut voir sur cette carte tous les biens de nos données. Chaque point noir représente un bien. Pour commencer les analyses descriptives, j’aime bien commencer par tous les variables qualitatives et ensuite faire les variables quantitatives. Cette méthode à pour moi 2 avantages, le premier est de suivre une liste et donc ne pas oublier de potentielles variables intéressantes, le second est qu’elle me permet de repérer des analyses bivariées potentiellement intéressantes à étudier.

glimpse(df) #liste de nos variables / types de nos variables
## Rows: 34,883
## Columns: 25
## $ id                        <dbl> 130420, 132994, 7397, 24260, 137112, 26562, …
## $ name                      <chr> "Charming Apartment 2BR in Paris 9e", "charm…
## $ host_id                   <dbl> 641777, 653074, 2626, 98012, 670637, 107514,…
## $ host_name                 <chr> "Yassine", "Victoire", "Franck", "Emmanuel",…
## $ Pays                      <chr> "France", "France", "France", "France", "Fra…
## $ host_is_superhost         <lgl> TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, FALS…
## $ host_total_listings_count <dbl> 1, 1, 8, 2, 4, 1, 1, 2, 2, 1, 2, 3, 1, 1, 2,…
## $ host_identity_verified    <lgl> TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, T…
## $ neighbourhood_cleansed    <chr> "Opéra", "Temple", "Hôtel-de-Ville", "Batign…
## $ latitude                  <dbl> 48.88073, 48.86341, 48.85909, 48.88090, 48.8…
## $ longitude                 <dbl> 2.342970, 2.358370, 2.353150, 2.303880, 2.38…
## $ property_type             <chr> "Entire rental unit", "Entire rental unit", …
## $ room_type                 <chr> "Entire home/apt", "Entire home/apt", "Entir…
## $ accommodates              <dbl> 6, 2, 4, 2, 2, 3, 2, 2, 2, 2, 2, 6, 2, 4, 3,…
## $ bedrooms                  <dbl> 2, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 3, 1, 1, 1,…
## $ beds                      <dbl> 3, 1, 2, 1, 1, 2, 2, 2, 1, 1, 1, 6, 1, 3, 2,…
## $ minimum_nights            <dbl> 1, 365, 10, 30, 3, 6, 4, 7, 3, 6, 90, 7, 4, …
## $ maximum_nights            <dbl> 30, 365, 130, 180, 365, 21, 730, 1125, 730, …
## $ has_availability          <lgl> TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TR…
## $ review_scores_location    <dbl> 4.81, 4.91, 4.93, 4.82, 4.72, 4.97, 4.64, 4.…
## $ review_scores_value       <dbl> 4.43, 4.50, 4.72, 4.64, 4.61, 4.98, 4.55, 4.…
## $ TypeBath                  <chr> "Private bath(s)", "Private bath(s)", "Priva…
## $ NbBath                    <dbl> 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,…
## $ Since                     <dbl> 4203, 4201, 5206, 4636, 4196, 4616, 4195, 46…
## $ PrixE                     <dbl> 208.74, 88.20, 106.82, 83.30, 117.60, 117.60…

Commencons par regarder où habite les propriétaires des biens de nos bases.

sum(df$Pays == "France")/ nrow(df)
## [1] 0.9636499

Sans surprise la France (Métropolitaine) est représentée à 96%. Mais qu’en est il des autres ? Comme la trace représente 96% on va tricher et rééquilibrer les fréquences, pour avoir une meilleure visualisation.

cnt =  as.data.frame(prop.table(table(df$Pays))*10000) 
#creation dataframe contenant la liste des pays et leur frequence d'apparition
cntorder <-  as.data.frame(prop.table(table(df$Pays))*100)
cntorder <- cntorder[order(-cntorder$Freq),]
#retravaille des frequence pour que se soit plus visuel sur la carte
cnt <- cnt %>% mutate(Freq = ifelse(Freq >100 , 100, Freq)
                      ,Freq = ifelse(Freq <100 , Freq+20, Freq)
                      )
names(cnt)[names(cnt) == 'Var1'] <- 'region'
cnt <- subset(cnt, cnt$region != "USA" )

wm <- map_data("world") #import d'un fichier contenant les pays et leur coordonnées GPS
cnt <- cnt %>% mutate(region = ifelse(as.character(region)=="United Kingdom", "UK" ,as.character(region))
                      ,region = ifelse(as.character(region)=="United States", "USA" ,as.character(region))
                      ) 

coord.map <- left_join(wm, cnt, by  = "region" )#on récupère les frequence d'apparition
ggplot(coord.map, aes(long, lat, group = group))+
  geom_polygon(aes(fill = Freq ), color = "white")+
  scale_fill_viridis_c(option = "C") + labs(title = "Carte des pays hôtes de nos propriétaires")

#ggsave("country_map.png", width = 11, height = 8)
#carte 
head(cntorder)
##              Var1       Freq
## 24         France 96.3649915
## 95  United States  0.7109480
## 94 United Kingdom  0.6966144
## 82          Spain  0.2064043
## 41          Italy  0.1978041
## 87    Switzerland  0.1720036

On peut voir sur notre carte que les propriétaires sont aussi présents à travers le monde, et en plus forte concentration aux Etats Unis, et en Europe. Même si 96% des propriétaires résident en France.

Regardons maintenant si nos propriétaires ont des comptes vérifiés et s’ils ont le label “superhost”.

ggplot(df, aes("", fill = factor(host_is_superhost))) + 
  geom_bar(aes(y = (..count..)/sum(..count..)), width = 1) +
  scale_y_continuous() +
  ylab("") + xlab("") + labs(fill = "host_is_superhost") +
  theme(axis.ticks = element_blank()) + 
  coord_polar(theta = "y") + theme_minimal() + labs(title = "Distribution de la variable host_is_superhost")

#ggsave("host_is_superhost.png", width = 11, height = 8)

ggplot(df, aes("", fill = factor(host_identity_verified))) + 
  geom_bar(aes(y = (..count..)/sum(..count..)), width = 1) +
  scale_y_continuous() +
  ylab("") + xlab("") + labs(fill = "host_identity_verified") +
  theme(axis.ticks = element_blank()) + 
  coord_polar(theta = "y") + theme_minimal() + labs(title = "Distribution de la variable host_identity_verified")

#ggsave("host_identity_verified.png", width = 11, height = 8)

On remarque sans surprise, une grande majorité de compte vérifié, mais à contrario, il y à une très faible proportion de “superhost”

qmplot(x=longitude, y=latitude, data = df, color = neighbourhood_cleansed) #carte des biens en focntion de leur quartier
## ℹ Using `zoom = 12`
## ℹ Map tiles by Stamen Design, under CC BY 3.0. Data by OpenStreetMap, under ODbL.

#ggsave("map_quartier.png")

Cette carte représente nos biens selon les quartiers de paris donné Airbnb. On va transformer les noms des arrondissment en numéros pour une meilleure lisibilité dans nos graphiques. Nous allons aussi crée un second dictionnaire avec le nom et le numéros de chaque arrondissement.

CorrQuartierArrondissment <- df %>% distinct(neighbourhood_cleansed)
df <- df %>% mutate(neighbourhood_cleansed = ifelse(neighbourhood_cleansed=="Batignolles-Monceau" , "17eme", neighbourhood_cleansed)
                    ,neighbourhood_cleansed = ifelse(neighbourhood_cleansed=="Bourse" , "2eme", neighbourhood_cleansed)
                    ,neighbourhood_cleansed = ifelse(neighbourhood_cleansed=="Opéra" , "9eme", neighbourhood_cleansed)
                    ,neighbourhood_cleansed = ifelse(neighbourhood_cleansed=="Temple" , "3eme", neighbourhood_cleansed)
                    ,neighbourhood_cleansed = ifelse(neighbourhood_cleansed=="Hôtel-de-Ville" , "3eme", neighbourhood_cleansed)
                    ,neighbourhood_cleansed = ifelse(neighbourhood_cleansed=="Popincourt" , "11eme", neighbourhood_cleansed)
                    ,neighbourhood_cleansed = ifelse(neighbourhood_cleansed=="Vaugirard" , "15eme", neighbourhood_cleansed)
                    ,neighbourhood_cleansed = ifelse(neighbourhood_cleansed=="Ménilmontant" , "20eme", neighbourhood_cleansed)
                    ,neighbourhood_cleansed = ifelse(neighbourhood_cleansed=="Buttes-Chaumont" , "19eme", neighbourhood_cleansed)
                    ,neighbourhood_cleansed = ifelse(neighbourhood_cleansed=="Passy" , "16eme", neighbourhood_cleansed)
                    ,neighbourhood_cleansed = ifelse(neighbourhood_cleansed=="Buttes-Montmartre" , "18eme", neighbourhood_cleansed)
                    ,neighbourhood_cleansed = ifelse(neighbourhood_cleansed=="Reuilly" , "12eme", neighbourhood_cleansed)
                    ,neighbourhood_cleansed = ifelse(neighbourhood_cleansed=="Panthéon" , "5eme", neighbourhood_cleansed)
                    ,neighbourhood_cleansed = ifelse(neighbourhood_cleansed=="Entrepôt" , "10eme", neighbourhood_cleansed)
                    ,neighbourhood_cleansed = ifelse(neighbourhood_cleansed=="Observatoire" , "14eme", neighbourhood_cleansed)
                    ,neighbourhood_cleansed = ifelse(neighbourhood_cleansed=="Palais-Bourbon" , "7eme", neighbourhood_cleansed)
                    ,neighbourhood_cleansed = ifelse(neighbourhood_cleansed=="Gobelins" , "13eme", neighbourhood_cleansed)
                    ,neighbourhood_cleansed = ifelse(neighbourhood_cleansed=="Louvre" , "1er", neighbourhood_cleansed)
                    ,neighbourhood_cleansed = ifelse(neighbourhood_cleansed=="Luxembourg" , "6eme", neighbourhood_cleansed)
                    ,neighbourhood_cleansed = ifelse(neighbourhood_cleansed=="Élysée" , "8eme", neighbourhood_cleansed)
                    
)

arrondiss <- c("9eme","3eme","4eme","17eme","11eme","15eme","20eme","19eme","16eme","18eme","12eme","5eme","10eme","14eme"
               ,"2eme","7eme","13eme","1er","6eme","8eme")


CorrQuartierArrondissment$NumArr <- arrondiss
ggplot(df,aes(neighbourhood_cleansed))+ geom_bar(aes(fill=neighbourhood_cleansed)) + geom_text(aes(label=..count..), stat="count", vjust = -0.5) + labs(title = "Distribution de la variable neighbourhood_cleansed", x = "Quartier")

#ggsave("map_quartier_bis.png", width = 11, height = 8)

On peut voir sur ce diagramme en barre la représentation des différents quartiers. On peut voir que c’est le quartier “Butte-Montmarte” qui est largement en tête avec plus de 6000 biens, alors que ce n’est pas le plus grand quartier si on se reporte à notre précédente carte. Le Louvre est le quartier le moins représenté.

nrd <-  as.data.frame(prop.table(table(df$neighbourhood_cleansed))*100)
nrd <- nrd[order(-nrd$Freq),]
nrdbis <- left_join(nrd,CorrQuartierArrondissment,by =c("Var1"="NumArr"))
nrdbis
##     Var1      Freq neighbourhood_cleansed
## 1  18eme 10.991027      Buttes-Montmartre
## 2  11eme  8.505576             Popincourt
## 3   3eme  7.055012                 Temple
## 4  15eme  7.009145              Vaugirard
## 5  17eme  7.000545    Batignolles-Monceau
## 6  10eme  6.966144               Entrepôt
## 7  19eme  6.636470        Buttes-Chaumont
## 8  16eme  6.455867                  Passy
## 9  20eme  6.364132           Ménilmontant
## 10 12eme  5.836654                Reuilly
## 11  9eme  4.265688                  Opéra
## 12 14eme  4.211220           Observatoire
## 13 13eme  3.956082               Gobelins
## 14  2eme  2.915460                 Bourse
## 15  5eme  2.852392               Panthéon
## 16  6eme  2.519852             Luxembourg
## 17  8eme  2.244646                 Élysée
## 18  7eme  2.184445         Palais-Bourbon
## 19   1er  2.029642                 Louvre

Regardons maintenant la variable property_type qui represente le type de bien.

nrd <-  as.data.frame(prop.table(table(df$property_type))*100)
nrd <- nrd[order(-nrd$Freq),]
nrd
##                                  Var1         Freq
## 12                 Entire rental unit 74.202333515
## 33        Private room in rental unit 10.148209730
## 5                        Entire condo  4.646962704
## 10                        Entire loft  2.098443368
## 41             Room in boutique hotel  1.742969355
## 8                         Entire home  0.997620617
## 25              Private room in condo  0.871484677
## 43                      Room in hotel  0.839950692
## 14                   Entire townhouse  0.659346960
## 13          Entire serviced apartment  0.516010664
## 21  Private room in bed and breakfast  0.478743227
## 29               Private room in home  0.478743227
## 52         Shared room in rental unit  0.355474013
## 36          Private room in townhouse  0.295272769
## 32               Private room in loft  0.266605510
## 24    Private room in casa particular  0.126135940
## 39                 Room in aparthotel  0.123269214
## 30             Private room in hostel  0.106068859
## 7                   Entire guesthouse  0.097468681
## 28         Private room in guesthouse  0.083135051
## 50              Shared room in hostel  0.074534874
## 49                Shared room in home  0.071668148
## 20                       Private room  0.068801422
## 56                          Tiny home  0.063067970
## 15               Entire vacation home  0.060201244
## 48               Shared room in condo  0.057334518
## 27        Private room in guest suite  0.054467792
## 6                  Entire guest suite  0.048734341
## 34 Private room in serviced apartment  0.048734341
## 16                       Entire villa  0.037267437
## 11                       Entire place  0.034400711
## 18                          Houseboat  0.034400711
## 40          Room in bed and breakfast  0.034400711
## 1                                Boat  0.025800533
## 44         Room in serviced apartment  0.020067081
## 51                Shared room in loft  0.017200355
## 9                     Entire home/apt  0.011466904
## 38              Private room in villa  0.011466904
## 22               Private room in boat  0.008600178
## 26       Private room in earthen home  0.008600178
## 46      Shared room in boutique hotel  0.008600178
## 3                        Earthen home  0.005733452
## 31          Private room in houseboat  0.005733452
## 35          Private room in tiny home  0.005733452
## 37      Private room in vacation home  0.005733452
## 42                     Room in hostel  0.005733452
## 53           Shared room in tiny home  0.005733452
## 54           Shared room in townhouse  0.005733452
## 2                                Cave  0.002866726
## 4            Entire bed and breakfast  0.002866726
## 17                              Floor  0.002866726
## 19                             Island  0.002866726
## 23          Private room in camper/rv  0.002866726
## 45   Shared room in bed and breakfast  0.002866726
## 47     Shared room in casa particular  0.002866726
## 55               Shared room in villa  0.002866726
## 57                              Tower  0.002866726

On remarque une très forte majorité de bien dit ” Entire rental unit”. Qu’en est-il de room_type (type de chambre).

ggplot(df,aes(room_type))+ geom_bar(aes(fill=room_type)) + geom_text(aes(label=..count..), stat="count", vjust = -0.5) + labs(title = "Distribution de la variable room_type", x = "Type de Chambre")

#ggsave("room_type.png", width = 11, height = 8)

C’est aussi “Entire home” qui domine largement, Il n’y a que très peu d’hôtels et de chambres partagées. Passons au type de salle de bain.

ggplot(df,aes(TypeBath))+ geom_bar(aes(fill=TypeBath)) + geom_text(aes(label=..count..), stat="count", vjust = -0.5) + labs(title = "Distribution de la variable TypeBath", x = "Type de salle de bain")

#ggsave("TypeBath.png", width = 11, height = 8)

On voit que presque tous les biens disposent d’au moins une salle de bain complète. On notera que 102 biens ne disposent que de half-bathroom, qui sont des salles de bain contenant un lavabo et des toilettes mais pas de baignoire ni de douche.

ggplot(df, aes(x = accommodates)) + geom_histogram() + geom_text(aes(label=..count..), stat="count", vjust = -0.5) + labs(title = "Distribution de la variable accommodates", x = "Nombre de personne que peut acceuillir un bien")
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

#ggsave("accommodates.png", width = 11, height = 8)
ggplot(df, aes(x = bedrooms)) + geom_histogram() + geom_text(aes(label=..count..), stat="count", vjust = -0.5) + labs(title = "Distribution de la variable bedrooms", x = "Nombre de chambre")
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

#ggsave("bedrooms.png", width = 11, height = 8)
ggplot(df, aes(x = beds)) + geom_histogram() + geom_text(aes(label=..count..), stat="count", vjust = -0.5) + labs(title = "Distribution de la variable beds", x = "Nombre de lit")
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

#ggsave("beds.png", width = 11, height = 8)
ggplot(df, aes(x = NbBath)) + geom_histogram() + geom_text(aes(label=..count..), stat="count", vjust = -0.5) + labs(title = "Distribution de la variable NbBath", x = "Nombre de salle de bain")
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

#ggsave("NbBath.png", width = 11, height = 8)
ggplot(df, aes(x = PrixE)) + geom_histogram() + labs(title = "Distribution de la variable PrixE", x = "Prix")
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

#ggsave("PrixE.png", width = 11, height = 8)

Il semblerait qu’il y ait quelques outliers dans nos données, 50 chambres dans un bien, 77 lits ou encore 12K€ la nuit. Cela semble assez étrange. L’avantage de nos données, c’est qu’elles sont réelles, il suffit donc de vérifier au près d’airbnb en tapant l’annonce sur Google.

L’annonce d’un certain Xavier possédant 50 chambre : https://www.airbnb.fr/rooms/8876983?source_impression_id=p3_1670436625_xKWnKnYPMpNTTNCa C’est en fait un hôtel qui loue des chambres donc l’hôtel possède probablement 50 chambres (encore que sur le site nous n’en voyons que 10) mais on loue d’une chambre.

L’annonce d’un certain Joffrey à 9000€ la nuit : https://www.airbnb.fr/rooms/30219412?source_impression_id=p3_1670437077_QyQCUJaZDPH%2BtO5O&check_in=2023-01-09&guests=1&adults=1&check_out=2023-01-15 Aujourd’hui le prix à la nuit était de 500€

Certaines annonces sont introuvables, elles ont peut être été supprimées ou modifiées.

summary(df$PrixE)
##     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
##     7.84    69.58   102.90   157.48   171.50 11760.00
a = df[order(-df$PrixE),]
a[c(1:10),c(2,8,9,13:16,21,25)]
## # A tibble: 10 × 9
##    name  host_identity_v… neighbourhood_c… room_type accommodates bedrooms  beds
##    <chr> <lgl>            <chr>            <chr>            <dbl>    <dbl> <dbl>
##  1 Appa… FALSE            19eme            Private …            6        3     3
##  2 Pari… TRUE             10eme            Entire h…            4        1     2
##  3 Amaz… TRUE             3eme             Entire h…            6        2     2
##  4 Amaz… FALSE            5eme             Entire h…            6        2     3
##  5 Amaz… FALSE            3eme             Entire h…            6        2     4
##  6 Amaz… FALSE            3eme             Entire h…           10        2     2
##  7 Cham… TRUE             15eme            Private …            2        1     1
##  8 Char… TRUE             15eme            Entire h…            4        1     2
##  9 Lumi… TRUE             14eme            Private …            2        1     1
## 10 Apar… TRUE             8eme             Entire h…            2        1     1
## # … with 2 more variables: review_scores_value <dbl>, PrixE <dbl>
df2 <- subset(df, df$PrixE <= 700 )
nrow(df2)/nrow(df)
## [1] 0.9840323

Pour nos futurs analyses nous allons conserver 98% des biens en gardant un prix inférieur à 700€. Nous supprimons également les outliers des chambres, lits et salles de bain.

df2 <- subset(df2, df2$bedrooms <= 10 )
df2 <- subset(df2, df2$beds <= 10 )
df2 <- subset(df2, df2$NbBath <= 10 )
ggplot(df2, aes(x = PrixE)) + 
  geom_histogram(aes(y = ..density..), binwidth = 30) +
  geom_density() + labs(title = "Distribution de la variable Prix", x = "Prix")

#ggsave("PrixE_bis.png", width = 11, height = 8)

C’est déjà plus lisible comme ca. On voit aussi que la grosse majorité des biens on un prix compris enrte 50 et 200€ la nuit.

ggplot(df2, aes(y = Since, x = "")) + geom_boxplot()+ labs(title = "Distribution de la variable Since", x = "Nombre de jour depuis l'inscription sur le site")

summary(df2$Since)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##      98    2165    2754    2615    3252    5341
#ggsave("Since.png", width = 11, height = 8)

Analyses multi-variées

Maintenant que l’on a fait un peu le tour des analyses descriptives, nous allons tenter de trouver de possibles liens entre nos variables par des analyses multi-variées. A noter que l’on ne vérifiera pas la véracité de nos propos par des tests statistiques.

dfinf = data.frame(
  neighbourhood_cleansed = table(df2$neighbourhood_cleansed)
)
names(dfinf)[names(dfinf) == 'neighbourhood_cleansed.Var1'] <- 'neighbourhood_cleansed'
names(dfinf)[names(dfinf) == 'neighbourhood_cleansed.Freq'] <- 'PrixM'
dfinf$PrixM = tapply(df2$PrixE, df2$neighbourhood_cleansed, mean)
dfinf$sdp = tapply(df2$PrixE, df2$neighbourhood_cleansed, sd)
ggplot(df2, aes(y = PrixE, x = neighbourhood_cleansed)) + geom_boxplot() + 
  geom_errorbar(data = dfinf, 
                aes(y = PrixM, ymin = PrixM - sdp, ymax = PrixM + sdp), 
                col = "red", width = .4) + labs(title = "Distribution de la variable neighbourhood_cleansed en fonction du prix", x = "Quartier")

#ggsave("neighbourhood_prix.png", width = 11, height = 8)

Sans surprise, se sont les quartiers au centre de Paris qui sont les plus cher, à savoir : Hotel-de-ville, le Louvre, et le Luxembourg. Ménilmontant est le quartier le moins cher.

df2$beds <- as.factor(df2$beds)
dfinf = data.frame(
  beds = table(df2$beds)
)
names(dfinf)[names(dfinf) == 'beds.Var1'] <- 'beds'
names(dfinf)[names(dfinf) == 'beds.Freq'] <- 'PrixM'
dfinf$PrixM = tapply(df2$PrixE, df2$beds, mean)
dfinf$sdp = tapply(df2$PrixE, df2$beds, sd)
ggplot(df2, aes(y = PrixE, x = beds)) + geom_boxplot()+ 
  geom_errorbar(data = dfinf, 
                aes(y = PrixM, ymin = PrixM - sdp, ymax = PrixM + sdp), 
                col = "red", width = .4) + labs(title = "Distribution de la variable beds en fonction du prix", x = "Nombre de lit")

#ggsave("beds_prix.png", width = 11, height = 8)
prop.table(table(df$beds))*100
## 
##            1            2            3            4            5            6 
## 49.623025543 31.416449273 11.079895651  4.710030674  1.811770777  0.776882722 
##            7            8            9           10           11           12 
##  0.255138606  0.160536651  0.057334518  0.025800533  0.020067081  0.014333630 
##           13           14           16           17           18           21 
##  0.005733452  0.002866726  0.005733452  0.005733452  0.005733452  0.002866726 
##           33           40           77           79           83           85 
##  0.002866726  0.002866726  0.002866726  0.002866726  0.002866726  0.002866726 
##           90 
##  0.002866726
df2$beds <- as.numeric(df2$beds)

On peut voir que plus le bien dispose de lit plus la moyenne des prix est élevée. En regardant le nombre de valeurs extrêmes pour les premières catégories de lit, je ne pense pas que se soit la variable qui influence le plus le prix.

df2$NbBath <- as.factor(df2$NbBath)
dfinf = data.frame(
  NbBath = table(df2$NbBath)
)
names(dfinf)[names(dfinf) == 'NbBath.Var1'] <- 'NbBath'
names(dfinf)[names(dfinf) == 'NbBath.Freq'] <- 'PrixM'
dfinf$PrixM = tapply(df2$PrixE, df2$NbBath, mean)
dfinf$sdp = tapply(df2$PrixE, df2$NbBath, sd)
ggplot(df2, aes(y = PrixE, x = NbBath)) + geom_boxplot()+ 
  geom_errorbar(data = dfinf, 
                aes(y = PrixM, ymin = PrixM - sdp, ymax = PrixM + sdp), 
                col = "red", width = .4) + labs(title = "Distribution de la variable NbBath en fonction du prix", x = "Nombre de salle de bain")

#ggsave("NbBath_prix.png", width = 11, height = 8)
prop.table(table(df$NbBath))*100
## 
##            0          0.5            1          1.5            2          2.5 
##  0.140469570  0.364074191 83.198119428  6.596336324  6.768339879  1.542298541 
##            3          3.5            4          4.5            5          5.5 
##  0.954619729  0.186337184  0.123269214  0.031533985  0.034400711  0.008600178 
##            6            7            8           20           50 
##  0.014333630  0.002866726  0.008600178  0.005733452  0.020067081
df2$NbBath <- as.numeric(df2$NbBath)

Même conclusion que précédemment, plus un bien a de salle de bain, plus le prix augmente. Ce qui semble assez logique finalement, car un studio de 20m2, moins cher qu’un grand duplex, ne peut pas contenir 6 salles de bain. Il aurait été intéressant de voir le prix au m2, c’est dommage de ne pas avoir cette information. On pourrait peut-être estimer taille du bien en le localisant précisément sur la carte et calculer la taille du bâtiment, mais cela dépasse malheureusement mes capacités.

dfinf = data.frame(
  room_type = table(df2$room_type)
)
names(dfinf)[names(dfinf) == 'room_type.Var1'] <- 'room_type'
names(dfinf)[names(dfinf) == 'room_type.Freq'] <- 'PrixM'
dfinf$PrixM = tapply(df2$PrixE, df2$room_type, mean)
dfinf$sdp = tapply(df2$PrixE, df2$room_type, sd)
ggplot(df2, aes(y = PrixE, x = room_type)) + geom_boxplot()+ 
  geom_errorbar(data = dfinf, 
                aes(y = PrixM, ymin = PrixM - sdp, ymax = PrixM + sdp), 
                col = "red", width = .4) + labs(title = "Distribution de la variable room_type en fonction du prix", x = "Type de bien")

#ggsave("room_type_prix.png", width = 11, height = 8)
prop.table(table(df$room_type))*100
## 
## Entire home/apt      Hotel room    Private room     Shared room 
##      83.6166614       1.2068916      14.5715678       0.6048792

Sans surprise là encore, les hôtels sont en moyenne les plus cher, suivi par les appartements, puis les chambres privées et enfin les chambres partagées. On remarque aussi beaucoup de valeurs extrêmes parmi nos catégories.

ggplot(df2, aes(factor(host_identity_verified), PrixE)) + 
  geom_boxplot() + labs(title = "Distribution de la variable host_identity_verified en fonction du prix", x = "Compte vérifié ou non")

#ggsave("host_verified_prix.png", width = 11, height = 8)

Ici on voit que le faite d’avoir un compte vérifé augmente très légèrement le prix.

ggplot(data=df) + geom_boxplot(aes(x=neighbourhood_cleansed, y= PrixE, fill=room_type)) + scale_y_log10() + geom_hline(yintercept = mean(df$PrixE), color="blue") + labs(title = "Distribution de la variable neighbourhood_cleansed en fonction du prix et du type de bien", x = "Quartier")

#ggsave("neighbourhood_prix_room_type.png", width = 11, height = 8)

Comme on le disait précédemment, les hôtels (en vert) sont en moyenne plus cher que les autres type de bien, peu importe le quartier. On voit aussi qu’il existe une différence de prix non négligeable entre les hôtels et les shared room (en violet). On voit également que la moyennes des prix des appartements, dans la plupart des quartiers est inférieure au prix moyen (137.5314€). C’est très intéressant.

Même si j’avais dit qu’on ne ferait pas de test statistique, nous allons quand même faire une matrice des corrélations pour les variables quantitatives. Pour les variables qualitatives on pourrait faire une anova non paramétrique, puisque je ne pense pas que nos données soient issues d’une population normale (gaussienne), mais ça serait déjà plus compliqué et on passerait le stade d’analyses descriptives.

df2 <- df2 %>% mutate(host_identity_verified = ifelse(as.character(host_identity_verified)=="TRUE", 1 ,0))
dfnum <-df2[,c(7,8,14:18,20,21,23:25)]
corr <- scale(dfnum)
ggcorr(corr,label=T, label_round = 2)

#ggsave("corr.png", width = 11, height = 8)

On voit très rapidement le lien (logique) qui existe entre les chambres, et le nombre de personne que peut accueillir un bien (accommodates) et le nombre de lit et le nombre de salle de bain. Dans les variables quantitatives (le quartier n’est donc pas représenté) c’est bien ces 4 dernières variables qui corrèlent positivement le plus le prix. le nombre de nuit maximum par réservation n’a aucune influence, positive ou négative sur le prix.

P_sf <- df2 %>%
  st_as_sf(coords = c("longitude", "latitude"))
grid<-st_make_grid(P_sf,what = "polygons",cellsize = .0015) #fabriquer les tuiles
grid2<-st_sf(grid) #transformer en objet geometry
grid2$row<-as.factor(row.names(grid2)) #ajouter un nom aux tuiles
#compter le nb d'hébergement par tuile
grid3 <- st_intersection(P_sf,grid2)
## Warning: attribute variables are assumed to be spatially constant throughout all
## geometries
#aggreger les prix par tuile ( n retient la médiane car la moyenne peut etre troublée par des valeurs extrêmes)
grid4<- aggregate(PrixE ~ row, data=grid3, FUN=mean)
#on merge
merged = merge(grid2, grid4, by = "row")
ggplot(merged)+ geom_sf(aes(fill=PrixE),colour=NA)+theme_minimal()+  scale_fill_gradient(low = "lightblue", high = "Black")

#ggsave("heat_map_tuiles.png", width = 11, height = 8)

Voici une représentation graphique des prix et chaque tuile représente une zone géographique. Les tuiles noires, qui représentent les prix les plus élevés, sont situées plus sur les bords de Seines, et donc au centre de Paris. C’est ce que l’on avait trouvé précédemment.

df$PrixE_rec <- cut(df$PrixE,
  include.lowest = TRUE,
  right = FALSE,
  dig.lab = 4,
  breaks = c(7.84, 62.72, 83.3, 107.8, 147, 229.32, 90094.4)
)

df$PrixE_rec <- as.character(df$PrixE_rec)
df$PrixE_rec[df$PrixE_rec == "[7.84,62.72)"] <- "very low"
df$PrixE_rec[df$PrixE_rec == "[62.72,83.3)"] <- "low"
df$PrixE_rec[df$PrixE_rec == "[83.3,107.8)"] <- "medium low"
df$PrixE_rec[df$PrixE_rec == "[107.8,147)"] <- "medium high"
df$PrixE_rec[df$PrixE_rec == "[147,229.3)"] <- "high"
df$PrixE_rec[df$PrixE_rec == "[229.3,9.009e+04]"] <- "very high"
df$PrixE_rec <- as.factor(df$PrixE_rec)

col = brewer.pal(n = 5, name = "Spectral")
colors <- colorFactor(col, domain = c("Very low", "low","medium low","medium high","high","very high"))
map_secteur_prix <- leaflet()
map_secteur_prix <- addTiles(map_secteur_prix)
map_secteur_prix <- addCircleMarkers(
  map = map_secteur_prix,
  lng = df$longitude,
  lat = df$latitude,
  popup = paste("Latitude:", df$latitude, ", longitude:", df$longitude),
  label = paste("Prix", df$PrixE),
  color = colors(df$PrixE_rec),
  radius = 1  # Rayon des points. On divise par 100, sinon les points sont trop gros.
)
## Warning in colors(df$PrixE_rec): Some values were outside the color scale and
## will be treated as NA
map_secteur_prix <- addProviderTiles(
  map = map_secteur_prix,
  provider = providers$Stamen.TonerLite
)
map_secteur_prix <- addLegend(
  map = map_secteur_prix,
  position = "topright",
  pal = colors,
  values = df$PrixE_rec,
  title = "Région",
  opacity = 1
)
## Warning in pal(v): Some values were outside the color scale and will be treated
## as NA
map_secteur_prix
colors <- colorNumeric(palette = "plasma", domain = df2$PrixE)
map_prix <- leaflet()
map_prix <- addTiles(map_prix)
map_prix <- addCircleMarkers(
  map = map_prix,
  lng = df2$longitude,
  lat = df2$latitude,
  popup = paste("Latitude:", df2$latitude, ", longitude:", df2$longitude),
  label = paste("Prix", df2$PrixE),
  color = colors(df2$PrixE),
  radius = 1  # Rayon des points. On divise par 100, sinon les points sont trop gros.
)
map_prix <- addProviderTiles(
  map = map_prix,
  provider = providers$Stamen.TonerLite
)
map_prix <- addLegend(
  map = map_prix,
  position = "topright",
  pal = colors,
  values = df$PrixE,
  title = "Région",
  opacity = 1
)
## Warning in pal(c(r[1], cuts, r[2])): Some values were outside the color scale
## and will be treated as NA
map_prix

Dans la continuité de la carte avec les tuiles de prix, je voulais faire une carte de Paris avec une heatmap des prix. Voila donc deux carte des prix avec des constructions différentes. Pour la première, le prix à été découpé par la foncion icut du package questionr en catégorie de prix, puis le point sur la carte qui représente le bien est teint en fonction de sa catégorie de prix. C’est très visuel car on voit directement où sont les biens les plus cher. Il faut quand même faire attention, les bien les plus cher (very high) sont en vert et les high en rouge, je n’ai pas réussi à corriger le problème. Mais cette première carte confirme ce que nous avons déjà vu, à savoir que les biens les plus cher se concentrent autour des 1,2,6,7 et 8ème arrondissement de Paris. La deuxième carte suit le même principe mais repose sur le prix en variable continue, le problème ici est qu’au vu de la grande hétérogénéité des prix et le très fort pourcentage de prix “faible”, on ne voit pas grand chose si on ne zoome pas.

res <- subset(df, df$has_availability==TRUE)
map_resa <- leaflet()
map_resa <- addTiles(map_resa)
map_resa <- addMarkers(
  map = map_resa,
  lng = res$longitude,
  lat = res$latitude,
  popup = paste(res$neighbourhood_cleansed, "Quartier"),
  clusterOptions = markerClusterOptions()
)
map_resa <- addProviderTiles(
  map = map_resa, 
  provider = providers$Stamen.TonerLite
)
map_resa <- addEasyButton(
  map = map_resa,
  button = easyButton(
    icon = "fa-globe",
    title = "Zoom initial",
    onClick = JS("function(btn, map){ map.setZoom(4); }")
  )
)
map_resa

Cette dernière carte est un peu différente, elle regroupe les biens qui ont des disponibilités pour leur réservation. Cette carte est construite en regroupant les biens par pacquets, qui se subdivise lorsque l’on zoom sur certains endroits, ce qui rend la navigation sur la carte beaucoup plus fluide. En rélalité cette carte n’apporte pas grand chose, mais je voulais juste essayer différentes facons de faire des cartes pour repésenter mes biens.

#Conclusion

Au final, nous avons vu au travers de cette analyse, que le prix d’une réservation était corrélé à plusieurs choses. Le quartier, qui est sans doute la variable la plus importante, le type de bien (hôtel, ou appartement), le nombre de chambre, de salle de bain, et de lit. Il serait intéressant d’analyser l’opposition de certaines variables sur l’influence du prix. On pourrait par exemple opposer le type de bien et les services proposés et ainsi voir si c’est réellement l’hôtel qui est le plus cher ou bien si se sont les services des hôtels qui font monter le prix. On pourrait également opposer des variables fortement corrélées entre elles, comme le nombre de pièce, la taille du bien et la taille moyenne d’une chambre et/ou d’une salle de bain. De cette façon on pourrait savoir ce qui influe vraiment sur le prix. En suivant ces pistes j’aurais pu réaliser une segmentation par une ACP pour mes variables numériques, et une AFCM pour compléter avec mes variables catégorielles. Une piste qui pourrait être intéressante serait de convertir la variable quartier en numérique pour pouvoir l’opposer aux autres variables numériques, pour cela on pourrait probablement compter le nombre de monuments par quartier et remplacer le nom par ce nombre. Même si on pouvait opposer ces variables en transformant nos variables numériques en catégorielles, je trouve intéressante la comparaison entre différentes méthodes de segmentation. Je n’ai jamais eu l’occasion au par avant de pouvoir travailler sur des données spatiales, et j’ai trouvé très intéressant de travailler ce genre de données, cette façon de pouvoir les représenter d’une manière différente de celle dont j’ai l’habitude, j’ai pu notamment découvrir la construction de carte.